/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ufpb.compiladores.sintatico.impl;

import java.util.Stack;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import ufpb.compiladores.lexico.LexicalTable;
import ufpb.compiladores.lexico.LexicalTable.Entry;
import ufpb.compiladores.sintatico.Clause;
import ufpb.compiladores.sintatico.Entity;
import ufpb.compiladores.sintatico.Enviroment;
import ufpb.compiladores.sintatico.Production;
import ufpb.compiladores.sintatico.SyntacticalAnalyzer;
import ufpb.compiladores.sintatico.graph.SyntaticalTree;

/**
 *
 * @author SemiNick
 */
public class SyntacticalAnalyzerRecursiveImpl implements SyntacticalAnalyzer {
    
    private final Enviroment env;
    private final LexicalTable lt;
    private String err_msg = null;
    private SyntaticalTree arv = null;
    private Stack<String> pilha = null;
    private boolean beg = false;
    private boolean com = false;
    
    public SyntacticalAnalyzerRecursiveImpl(Enviroment env, LexicalTable lt) {
    
       if (env == null) {
           
           throw new NullPointerException("Enviroment cannot be null");
       } else if (lt == null) {
           
           throw new NullPointerException("LexicalTable cannot be null");
       }
       
       this.env = env;
       this.lt = lt;
       this.arv = new SyntaticalTree();
       this.pilha = new Stack<String>();
    }
    
    @Override
    public void processEnviroment(Document doc, Element r) {
    
        if (processProduction(this.env.getInitialProduction(), lt)) {
            
            //System.out.println("ERROR: " + err_msg);
            if (this.lt.getNext(1) != null) {

                System.out.println("ERROR: tokens after end");
            }
        }
    }

    @Override
    public SyntaticalTree getSyntaticalTree(){

        return this.arv;
    }
    
    private boolean processProduction(Production p, LexicalTable lt) {
        
        boolean ok = true;
        Clause[] clauses = p.getClauses();



        laco_c:        
        for (int ci = 0; ci < clauses.length; ci++) {
            
            ok = true;
            Clause c = clauses[ci];
            Entity[] ents = c.getEntities();
            laco_e:
            for (int ei = 0; ei < ents.length; ei++) {

                Entity e = ents[ei];
                switch (e.getType()) {
                    
                    case PRODUCTION:
                        
                        if (!this.processProduction(
                                this.env.getProduction(e.value()), lt)) {
                            
                            ok = false;
                            //continue laco_c;
                            break laco_e;
                        }
                        break;
                        
                    case TOKEN:
                        
                        if (lt.getNext(1) != null) {
                            
                            Entry et = lt.getNext(1);
                            if (!et.getToken().equalsIgnoreCase(e.value())) {
                                
                                this.err_msg = "Expecting: " + e.value() + " but "
                                                + et.getToken() + " found at line: " + et.getLine();
                                if (ei > 0) {
                                    
                                    System.out.println("Expecting: " + e.value() + " but "
                                        + et.getToken() + " found at line: " + et.getLine());
                                }
                                ok = false;
                                //continue laco_c;
                                break laco_e;
                            } else {

                                this.lt.next();

                                if (lt.getCurrent().getToken().equalsIgnoreCase("begin")){

                                    if (!beg){

                                        arv.addBegin();
                                        beg = true;
                                    } else {

                                        pilha.push(pilha.pop()+"1");
                                    }
                                } else if (lt.getCurrent().getToken().equalsIgnoreCase("while")){

                                    pilha.push("while");
                                    int a=1;
                                    String condicao = "while ";
                                    while (!lt.getNext(a).getToken().equalsIgnoreCase("do")){

                                        condicao = condicao + lt.getNext(a).getToken() + " ";
                                        a++;
                                    }
                                    String st = new String();
                                    st = condicao.substring (0, condicao.length() - 1);
                                    arv.addWhile(st);
                                } else if (lt.getCurrent().getToken().equalsIgnoreCase("if")){

                                    pilha.push("if");
                                    int a=1;
                                    String condicao = "if ";
                                    while (!lt.getNext(a).getToken().equalsIgnoreCase("then")){

                                        condicao = condicao + lt.getNext(a).getToken() + " ";
                                        a++;
                                    }

                                    String st = new String();
                                    st = condicao.substring (0, condicao.length() - 1);
                                    arv.addIf(st);
                                } else if (lt.getCurrent().getToken().equalsIgnoreCase("else")){

                                    pilha.push("else");
                                    arv.addElse();
                                } else if (lt.getCurrent().getToken().equals("end") &&
                                        !lt.getNext(1).getToken().equals(".")){

                                    if (pilha.peek().contains("if")){

                                        arv.addPoint();
                                        if (!lt.getNext(1).getToken().equalsIgnoreCase("else")){

                                            arv.fechaIf();
                                        }
                                        pilha.pop();
                                    } else if (pilha.peek().contains("else")){

                                        arv.fechaElse();
                                        pilha.pop();
                                    } else if (pilha.peek().contains("while")){

                                        arv.fechaWhile();
                                        pilha.pop();
                                    }
                                } else if (lt.getCurrent().getToken().equalsIgnoreCase("end") &&
                                        lt.getNext(1).getToken().equals(".")){

                                    arv.addEnd();
                                } else if (lt.getCurrent().getToken().equals(";") &&
                                        !lt.getNext(-1).getToken().equalsIgnoreCase("end")){

                                    com = false;
                                } else if (lt.getCurrent().getToken().equals(";") &&
                                        lt.getNext(-1).getToken().equalsIgnoreCase("end")){

                                    ;
                                } else {

                                    if (beg && !com && pilha.isEmpty()
                                            && !lt.getNext(-1).getToken().equalsIgnoreCase("end")) {

                                        com = true;
                                        int a=-1;
                                        String comando = "";
                                        while (!lt.getNext(a).getToken().equals(";")){

                                            comando = comando + lt.getNext(a).getToken() + " ";
                                            a++;
                                        }
                                        String st = new String();
                                        st = comando.substring (0, comando.length() - 1);
                                        arv.addCommand(st);
                                    } else if (beg && !com && !pilha.isEmpty()){

                                        if (lt.getCurrent().getToken().equalsIgnoreCase("begin")){

                                            String comando = "";
                                            int a = 1;
                                            while (!lt.getNext(a).getToken().equals(";")){

                                                comando = comando + lt.getNext(a).getToken() + " ";
                                                a++;
                                            }

                                            String st = new String();
                                            st = comando.substring (0, comando.length() - 1);

                                            arv.addCommand(st);
                                        } else if (pilha.peek().contains("1")){

                                            com = true;
                                            int a=-1;
                                            String comando = "";
                                            while (!lt.getNext(a).getToken().equals(";")){

                                                comando = comando + lt.getNext(a).getToken() + " ";
                                                a++;
                                            }

                                            String st = new String();
                                            st = comando.substring (0, comando.length() - 1);
                                            
                                            arv.addCommand(st);

                                        }
                                    }
                                }

                            }
                        } else {
                            
                            this.err_msg = "Expecting: " + e.value() + " but nothing found";
                            ok = false;
                            break laco_e;                            
                        }
                        break;
                        
                    case TYPE:
                        
                        if (lt.getNext(1) != null) { 
                            
                            Entry et = lt.getNext(1);
                            if (!et.getDescription().equalsIgnoreCase(e.value())) {
                                
                                this.err_msg = "Expecting: " + e.value() + " but "
                                        + et.getToken() + " found at line: " + et.getLine();
                                if (ei > 0) {
                                    
                                    System.out.println("ERRORi: " + "Expecting: " + e.value() + " but "
                                        + et.getToken() + " found at line: " + et.getLine());
                                }
                                ok = false;
                                //continue laco_c;
                                break laco_e;
                            } else {
                                
                                this.lt.next();
                            }
                        } else {
                            
                            this.err_msg = "Expecting: " + e.value() + " but nothing found";
                            ok = false;
                            break laco_e;                            
                        }
                        break;
                        
                    case VOID:
                        
                        break;
                }
            }
            
            //Processou todas as entidades sem dar erro então já processamos a produção
            if (ok) {
                
                return true;
            }
        }
        
        //Gerou erro, mas produção não é opcional
        if (!ok && !p.isOptional()) {

            System.out.println("ERROR " + this.err_msg);
        }
        
        return ok;
    }
}
